#include "lcd_control.h"
#include "../s3c2440_soc.h"

#define HCLK 100

void lcd_gpio_init(void)
{
	//初始化背光GPIO为OUT,先清0，后置1
	GPCCON &= ~(3<<0);
	GPCCON |= (1<<0);

	//GPCCON 0x56000020
	//GPDCON 0x56000030
	GPCCON = 0xaaaaaaaa;	//一个a表示0x1010，有4bit
	GPDCON = 0xaaaaaaaa;

	//GPG4  LCD_PWR
	GPGCON |= (3<<8);
}

void s3c2440_lcd_control_init(p_lcd_params params)
{
	int pixelplace; //像素格式？？？
	unsigned int addr;
/*	LCDCON1 0X4D000000
 *	CLKVAL [17:8]	决定VCLK 的频率和CLKVAL[9:0]。 TFT：VCLK = HCLK / [(CLKVAL + 1) × 2] （CLKVAL≥0）
 *					VCLK 就是 pixel_clk(Dot Data Clock--DCLK--9.0--15 MHz),9MHz = 100MHz/((X+1)*2) --> X = (HCLK/VCLK)/2-1
 *	MMODE [7]		决定VM 的触发频率			0 = 每帧 ---默认值就可以！！
 * 	PNRMODE [6:5]	选择显示模式				11 = TFT LCD 面板
 *	BPPMODE [4:1]	选择BPP（位每像素）模式	1100 = TFT 的16 bpp  
 *	ENVID [0]---放在enable/disable中控制----LCD 视频输出和逻辑使能/禁止。	0 = 禁止视频输出和LCD 控制信号 1 = 允许视频输出和LCD 控制信号
*/
	int CLKVAL = 5;	//(double)HCLK/params->lcd_sq.pixel_clk/2-1+0.5;  //从double类型，转为int，去掉尾巴---无法直接计算使用，要最终值
	int bpp_mode = params->bpp == 8 ? 0xb :\
				   params->bpp == 16 ? 0xc : \
				   0xd;	//params->bpp == 24

	LCDCON1 = (CLKVAL<<8) | (0<<7) | (3<<5) | (bpp_mode<<4);

/*	LCDCON2 0X4D000004
 *	VBPD	[31:24]		TFT：垂直后沿为帧开始时，垂直同步周期后的的无效行数。
 *	LINEVAL	[23:14]		TFT/STN：此位决定了LCD 面板的垂直尺寸。
 *	VFPD	[13:6]		TFT：垂直前沿为帧结束时，垂直同步周期前的的无效行数。
 *	VSPW	[5:0]		TFT：通过计算无效行数垂直同步脉冲宽度决定VSYNC 脉冲的高电平宽度。
*/
	LCDCON2 = ((params->lcd_squence.tvb - 1)<<24) | \
			  ((params->yres - 1)<<14) | \
			  ((params->lcd_squence.tvf - 1)<<6) | \
			  ((params->lcd_squence.tvp - 1)<<0);

/*	LCDCON3 0X4D000008
 *	HBPD(TFT)	[25:19]		TFT：水平后沿为HSYNC 的下降沿与有效数据的开始之间的VCLK 周期数。
 *	HOZVAL		[18:8]		TFT/STN：此位决定了LCD 面板的水平尺寸。必须决定HOZVAL来满足1 行的总字节为4n 字节。如果单色模式中LCD 的x 尺寸为120
							个点，但不能支持x=120，因为1 行是由16 字节（2n）所组成。LCD面板驱动器将舍弃额外的8 个点。
 *	HFPD(TFT)	[7:0]		TFT：水平后沿为有效数据的结束与HSYNC 的上升沿之间的VCLK 周期数。
*/
	LCDCON3 = ((params->lcd_squence.thb - 1)<<19) | \
			  ((params->xres - 1)<<8) | \
			  ((params->lcd_squence.thf - 1)<<0);

/*	LCDCON4 0X4D00000C
 *	HSPW(TFT)	[7:0]		TFT：通过计算VCLK 的数水平同步脉冲宽度决定HSYNC 脉冲的高电平宽度
*/
	LCDCON4 = ((params->lcd_squence.thp - 1)<<0);

/*	LCDCON5 0X4D000010  控制极性！！！
 *	BPP24BL [12]	TFT：	 此位决定24 bpp 视频存储器的顺序					0 = LSB 有效 1 = MSB 有效
 *	FRM565 	[11]	TFT：	 此位选择16 bpp 输出视频数据的格式					0 = 5:5:5:1 格式 1 = 5:6:5 格式
 *	INVVCLK [10]	STN/TFT：此位控制VCLK 有效沿的极性							0 = VCLK 下降沿取视频数据 1 = VCLK 上升沿取视频数据
 *	INVVLINE[9]		STN/TFT：此位表明VLINE/HSYNC 脉冲极性					0 = 正常 1 = 反转
 *	INVVD 	[7]		STN/TFT：此位表明VFRAME/VSYNC 脉冲						0 = 正常 1 = 反转VD
 *	INVVDEN [6]		TFT：	 此位表明VDEN 信号极性							0 = 正常 1 = 反转
 *	INVPWREN[5]		STN/TFT：此位表明PWREN 信号极性							0 = 正常 1 = 反转
 //以下不设置
 *	INVLEND [4]		TFT：	 此位表明LEND 信号极性							0 = 正常 1 = 反转
 *	PWREN 	[3]		STN/TFT：LCD_PWREN 输出信号使能/禁止						0 = 禁止PWREN 信号 1 = 允许PWREN 信号
 *	ENLEND 	[2]		TFT：	 LEND 输出信号使能/禁止							0 = 禁止LEND 信号 1 = 允许LEND 信号
 *	BSWP 	[1]		STN/TFT：字节交换控制位									0 = 交换禁止 1 = 交换使能
 *	HWSWP 	[0]		STN/TFT：半字节交换控制位								0 = 交换禁止 1 = 交换使能
*/
	///////?????????不明白这段什么意思???????????//////////
	pixelplace = params->bpp == 32 ? (0) : \
	             params->bpp == 16 ? (1) : \
	             (1<<1);  /* 8bpp */

	LCDCON5 = (params->lcd_polarity.pixel_clk<<10) |\
			  (params->lcd_polarity.hsync<<9) |\
			  (params->lcd_polarity.vsync<<8) |\
			  (params->lcd_polarity.vd_de<<6) |\
			  (params->lcd_polarity.power_en<<5) |\
			  (1<<11) | pixelplace;

/* 	LCDSADDR1 0X4D000014  --> [29:0] -- A[30:1] 只保留1-30，去掉0位、31位
 *	LCDBANK [29:21]--A[30:22]
 *	LCDBASEU [20:0]--A[21:1]
*/
	addr = (params->fb_base) & ~(1<<31);	//最高位置0，去掉最高位
	addr >>= 1;		//去掉最低位
	LCDSADDR1 = addr;	//开始地址

/*	LCDSADDR2 0X4D000018
 *	LCDBASEL [20:0]	-- A[21:1]
 *	LCDBASEL = ((帧结束地址) >> 1) + 1 = LCDBASEU + (PAGEWIDTH+OFFSIZE) × (LINEVAL+1)
*/
	addr = params->fb_base + params->xres*params->yres*params->bpp/8;	//总的偏移
	addr >>= 1;
	addr &= 0x1fffff;	//1个f是4bit，5个f是20bit
	LCDSADDR2 = addr;	//结束地址
}

void s3c2440_lcd_control_enable(void)
{
	//开启背光
	GPCDAT |= (1<<0);

	//LCDCON5	先供电，PWREN [3]STN/TFT：LCD_PWREN 输出信号使能/禁止	0 = 禁止PWREN 信号 1 = 允许PWREN 信号
	LCDCON5 |= (1<<3);

	//LCDCON1	在配置控制器，开启控制器	LCD video output and the logic enable/disable.
	LCDCON1 |= (1<<0);	
}

void s3c2440_lcd_control_disable(void)
{
	//关闭背光
	GPCDAT &= ~(1<<0);

	//LCDCON1	先关闭控制器	LCD video output and the logic enable/disable.
	LCDCON1 &= ~(1<<0);

	//LCDCON5	再关闭电源 PWREN [3]STN/TFT：LCD_PWREN 输出信号使能/禁止	0 = 禁止PWREN 信号 1 = 允许PWREN 信号
	LCDCON5 &= ~(0<<3);
}

lcd_controler s3c2440_lcd_control = {		//引用要等号
	.name	 = "s3c2440",
	.init	 = s3c2440_lcd_control_init,
	.enable	 = s3c2440_lcd_control_enable,
	.disable = s3c2440_lcd_control_disable
};											//要分号结束

void s3c2440_lcd_control_add(void)
{
	register_lcd_controler(&s3c2440_lcd_control);
}

